package milu.dbxml;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import milu.file.Collector;

import org.apache.log4j.Logger;

import com.sleepycat.dbxml.XmlContainer;
import com.sleepycat.dbxml.XmlException;
import com.sleepycat.dbxml.XmlInputStream;
import com.sleepycat.dbxml.XmlManager;
import com.sleepycat.dbxml.XmlTransaction;

public class Import {
	final static Logger log = Logger.getLogger(Import.class);
	private final DbXmlContext dbxml;
	private final String lockDocName;
	private static final Pattern fileNameIdRex =
			Pattern.compile("\\d{4}-\\d\\d-\\d\\d-\\d+-(\\d+)");

	private List<File> files = new ArrayList<File>();

	public Import(File f, String[] entries, DbXmlContext dbxml,
			String lockDocName) {
		this.dbxml = dbxml;
		this.lockDocName = lockDocName;
		for (String e : entries) {
			File f2 = new File(f.getAbsolutePath() + File.separator + e);
			files.add(f2);
		}
	}

	public void blabla() {
		Import.Report report = new Import.Report();
		for (File f : files) {
			log.info("verarbeite " + f.getAbsolutePath());
			FileFilter ff = new FileFilter() {
				public boolean accept(File f) {
					return f.isDirectory() || f.getName().endsWith(".xml");
				}
			};
			Collector coll = new Collector(f, ff);
			List<File> batch = new ArrayList<File>();
			while (coll.hasNext()) {
				File f2 = coll.nextFile();
				if (null == f2) // XXX lieber hasNextFile() implementieren
					break;
				log.debug(f2 + "\t" + coll.getCurrentQueueSize());
				batch.add(f2);
				if (batch.size() >= 10) {
					process(batch, report);
					batch.clear();
				}
			}
			if (batch.size() > 0) {
				process(batch, report);
				batch.clear();
			}
		}
		log.info("fertig");
		log.info(report);
		dbxml.deleteDoc(this.lockDocName);
	}

	/*
	 * XXX Soll der Name des Dokumentes wirklich aus den Namen der Datei gelesen
	 * werden? Oder lieber das Dokument lesen und den Namen aus @ID holen?
	 */
	private void process(List<File> files, Import.Report report) {
		XmlManager mgr = dbxml.getManager();
		XmlContainer cont = dbxml.getContainer();
		XmlTransaction txn = null;
		List<String> inserted = new ArrayList<String>();
		String docName = null;
		try {
			txn = mgr.createTransaction();
			for (File f : files) {
				Matcher matcher = fileNameIdRex.matcher(f.getName());
				if (matcher.lookingAt())
					docName = matcher.group(1);
				else
					docName = f.getName();
				log.debug(f.getName());
				FileInputStream fis = new FileInputStream(f);
				XmlInputStream xis = mgr.createInputStream(fis);
				try {
					cont.putDocument(txn, docName, xis);
					inserted.add(f.getName());
					report.inserted++;
				} catch (XmlException e) {
					report.errcount++;
					switch (e.getErrorCode()) {
					case XmlException.UNIQUE_ERROR:
						report.dupcount++;
						log.debug("Dokument doppelt: " + e.getMessage());
						break;
					default:
						log.error(e.getMessage());
						e.printStackTrace();
						break;
					}
				}
			}
			txn.commit();
		} catch (XmlException e) {
			if (txn != null) {
				try {
					txn.abort();
					txn.delete();
				} catch (XmlException e2) {
					e2.printStackTrace();
				}
			}
		} catch (FileNotFoundException e) {
			log.warn(e.getMessage());
		} finally {
			log.debug("eingespielte Dokumente: " + inserted.size());
			for (String s : inserted)
				log.debug("* " + s);
		}
	}

	private class Report {
		private int errcount;
		private int dupcount;
		private int inserted;

		@Override public String toString() {
			return "Erfolg: " + inserted + ", Fehler: " + errcount
					+ ", davon Doubletten: " + dupcount;
		}
	}
}
